import os from 'node:os';
import path from 'node:path';
import util from 'node:util';
import fs from 'node:fs';
import https from 'node:https';
import extract from 'extract-zip';

const CHROMIUM_PATH = path.join(import.meta.dirname, '..', '..', '..', '.local-chromium');

const downloadURLs = {
    linux: 'https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/%d/chrome-linux.zip',
    darwin: 'https://storage.googleapis.com/chromium-browser-snapshots/Mac/%d/chrome-mac.zip',
    win32: 'https://storage.googleapis.com/chromium-browser-snapshots/Win/%d/chrome-win32.zip',
    win64: 'https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/%d/chrome-win32.zip',
};

// 第一步，下载
async function downloadChromium(revision, progressCallback) {
    let url = null;

    const platform = os.platform();
    if (platform === 'darwin')
        url = downloadURLs.darwin;
    else if (platform === 'linux')
        url = downloadURLs.linux;
    else if (platform === 'win32')
        url = os.arch() === 'x64' ? downloadURLs.win64 : downloadURLs.win32;

    // console.assert 就是第一个参数的值为 false 的时候，才输出第二个参数的信息
    console.assert(url, `Unsupported platform: ${platform}`);

    // util.format 是格式化字符串用的，有一些占位符，%d 是数字、%s 是字符串、%j 是 JSON 等
    url = util.format(url, revision);

    // 实现第二步，解压缩
    // 这个自己处理就比较麻烦了，直接用第三方的包就行，比如 extract-zip, 它会解压 zip 文件到目标目录
    const zipPath = path.join(CHROMIUM_PATH, `download-${revision}.zip`);
    const folderPath = path.join(CHROMIUM_PATH, revision);
    // 首先确定 zip 包的路径和解压到的目录的路径，如果目录已经存在了，那就不下载了。否则调用刚才实现的两个方法来下载 zip 和解压缩。
    if (fs.existsSync(folderPath)) {
        return;
    }
    try {
        if (!fs.existsSync(CHROMIUM_PATH)) {
            fs.mkdirSync(CHROMIUM_PATH);
        }

        await downloadFile(url, zipPath, progressCallback);
        await extractZip(zipPath, folderPath);
    } catch (e) { }
}

function downloadFile(url, destinationPath, progressCallback) {
    let resolve, reject;
    const promise = new Promise((x, y) => { resolve = x; reject = y; })
    const request = https.get(url, response => {
        //  response 参数是一个流。因为如果数据很多，需要等好久才能传完，那要等全部传完再处理么？不用，可以每传一部分就处理一部分。这就是流的思想。
        // 基本所有语言处理网络和文件 IO 的 api 都是基于流的，
        if (response.statusCode !== 200) {
            const error = new Error(`Download failed: server returned code ${response.statusCode}. URL: ${url}`);
            // 失败的时候，流中的数据就不需要了，所以要调用 response.resume() 来消费掉
            response.resume();
            reject(error);
            return;
        }

        // 我们创建了一个写入流，写入到本地的文件的
        const file = fs.createWriteStream(destinationPath);

        file.on('finish', () => resolve());
        file.on('error', error => reject(error));

        // 然后把响应流 pipe 到文件流，也就是直接写入到文件里了：
        response.pipe(file);
        const totalBytes = parseInt(response.headers['content-length'], 10);
        if (progressCallback)
            response.on('data', onData.bind(null, totalBytes));
        
    });
    function onData(totalBytes, chunk) {
        progressCallback(totalBytes, chunk.length);
    }
    request.on('error', error => reject(error));
    return promise;
}



function extractZip(zipPath, folderPath) {
    return new Promise(resolve => extract(zipPath, { dir: folderPath }, resolve));
}


function executablePath(revision) {
    const platform = os.platform();
    if (platform === 'darwin')
        return path.join(CHROMIUM_PATH, revision, 'chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium');
    if (platform === 'linux')
        return path.join(CHROMIUM_PATH, revision, 'chrome-linux', 'chrome');
    if (platform === 'win32')
        return path.join(CHROMIUM_PATH, revision, 'chrome-win32', 'chrome.exe');
    throw new Error(`Unsupported platform: ${platform}`);
}

// 导出 downloadChromium 和拿到下载后的 chrome 的可执行文件路径的方法
export {
    executablePath,
    downloadChromium
}